
#include "member_BXmlNode.h"
#include "member_BHtmlItem.h"
#include "member_BHtmlWidget.h"
#include "member_BObject.h"

using namespace BWE;

BMap<char, BString>& InitConvertCodeMap()
{
	static BMap<char, BString>	transCodeMap;
	if (transCodeMap.empty())
	{
		transCodeMap['<'] = "&lt;";
		transCodeMap['>'] = "&gt;";
		transCodeMap['\"'] = "&quot;";
		transCodeMap['&'] = "&amp;";
	}
	return transCodeMap;
}
bool BWE::ConvertTextToHtml(BString& text)
{
	BMap<char, BString>& codeMap = InitConvertCodeMap();
	bool changed = false;
	for (auto it = codeMap.begin(); it.valid(); it++)
	{
		if(text.replace(it.key(), it.value()) > 0)
			changed = true;
	}
	return changed;
}
bool BWE::ConvertHtmlToText(BString& text)
{
	BMap<char, BString>& codeMap = InitConvertCodeMap();
	bool changed = false;
	for (auto it = codeMap.begin(); it.valid(); it++)
	{
		if(text.replace(it.value(), it.key()) > 0)
			changed = true;
	}
	return changed;
}

member_BHtmlItem::member_BHtmlItem(BHtmlItem* htmlItem)
{
	boss = htmlItem;
	htmlWidget = 0;
	align = Align_Left;
	spacing = 3;
	dirtySize = false;
}
member_BHtmlItem::~member_BHtmlItem()
{

}

void member_BHtmlItem::dirtyHtml()
{
	if (htmlWidget)
	{
		dirtySize = true;
		block.dirtyCells = true;
		html_member(htmlWidget)->dirty = true;
		object_member(htmlWidget)->emit(Signal_ItemChanged, boss);
		htmlWidget->fresh();
	}
}

void member_BHtmlItem::adjustRect(const BPoint& pos)
{
	rect += pos;
	block.rect += pos;

	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;

	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* child = dynamic_cast<BHtmlItem*>(children[i].ptr());
		item_member(child)->adjustRect(rect.pos());
	}
}

BSize member_BHtmlItem::freshSize(int width)
{
	const BString& name = boss->name();
	if (name == '!')
		return BSize();

	int left = 0;
	int top = 0;

	block.dirtyCells = true;
	block.rect.setPos(left, top);
	block.adjust(width);

	if (!htmlWidget->readOnly() && block.rect.width() < 1)
	{
		block.rect.setWidth(block.font()->size());
	}

	rect.setPos(left, top);
	rect.setSize(block.rect.size());
	bool is_head = name.size() == 2 && name[0] == 'h' && name[1] >= '1' && name[1] <= '6';
	if (is_head || name == "div" || name == 'p')
	{
		rect.setWidth(width);
	}
	if (name == "table")
	{
		freshTableSize(width);
	}
	else if (name == "li" || name == "dl")
	{
		freshListSize(width);
	}
	else
	{
		if (rect.width() < block.rect.width())
			rect.setWidth(block.rect.width());

		BArray<BXmlNodeHolder>& children = xml_member(boss)->children;

		BRect rowRect(left, top, block.rect.width(), block.rect.height() + spacing);
		for (int i = 0; i < children.size(); i++)
		{
			BHtmlItem* child = dynamic_cast<BHtmlItem*>(children[i].ptr());
			const BString& childName = child->name();
			if (childName == '!')
				continue;
			if (childName == "br")
			{
				BSize childSize = item_member(child)->freshSize(0);
				item_member(child)->adjust(rowRect.rightTop(), childSize);
				rowRect.right() += childSize.width();
				if (rect.right() < left + rowRect.width())
					rect.setWidth(left + rowRect.width());
				rect.bottom() = rowRect.bottom();
				rowRect.set(left, rect.bottom(), 0, 0);
				continue;
			}
			if (childName == "span")
			{
				BSize childSize = item_member(child)->freshSize(0);
				item_member(child)->adjust(rowRect.rightTop(), childSize);
				rowRect.right() += childSize.width();
				if (rowRect.height() < childSize.height())
					rowRect.setHeight(childSize.height());
				if (rect.width() < rowRect.width())
					rect.setWidth(rowRect.width());
				continue;
			}
			if (childName.size() == 2 && childName[0] == 'h' && childName[1] >= '1' && childName[1] <= '6')
			{
				BSize childSize = item_member(child)->freshSize(width);
				rect.bottom() = rowRect.bottom();
				item_member(child)->adjust(rect.leftBottom(), childSize);
				rect.bottom() += childSize.height();
				if (rect.right() < left + childSize.width())
					rect.right() = left + childSize.width();
				rowRect.set(left, rect.bottom(), 0, 0);
				continue;
			}
			if (childName == "div" || childName == 'p')
			{
				BSize childSize = item_member(child)->freshSize(width);
				rect.bottom() = rowRect.bottom();
				item_member(child)->adjust(rect.leftBottom(), childSize);
				rect.bottom() += childSize.height();
				if (rect.right() < left + childSize.width())
					rect.right() = left + childSize.width();
				rowRect.set(left, rect.bottom(), 0, 0);
				continue;
			}
			if (childName == "dl")
			{
				BSize childSize = item_member(child)->freshSize(width);
				rect.bottom() = rowRect.bottom();
				item_member(child)->adjust(rect.leftBottom(), childSize);
				rect.bottom() += childSize.height();
				if (rect.right() < left + childSize.width())
					rect.right() = left + childSize.width();
				rowRect.set(left, rect.bottom(), 0, 0);
				continue;
			}
			if (name == "pre")
			{
				if (xml_member(child)->format.head_prev)
					rowRect.set(left, rect.bottom(), 0, 0);
				BSize childSize = item_member(child)->freshSize(0);
				item_member(child)->adjust(rowRect.rightTop(), childSize);
				rowRect.right() += childSize.width();
				if (rowRect.height() < childSize.height())
					rowRect.setHeight(childSize.height());
				if (rect.right() < rowRect.right())
					rect.right() = rowRect.right();
				if (rect.bottom() < rowRect.bottom())
					rect.bottom() = rowRect.bottom();
				continue;
			}
			BSize childSize = item_member(child)->freshSize(width - rowRect.width());
			item_member(child)->adjust(rowRect.rightTop(), childSize);
			rowRect.right() += childSize.width();
			if (rowRect.height() < childSize.height())
				rowRect.setHeight(childSize.height());
			if (rect.right() < rowRect.right())
				rect.right() = rowRect.right();
			if (rect.bottom() < rowRect.bottom())
				rect.bottom() = rowRect.bottom();
		}
	}

	dirtySize = false;

	if (fixedWidth > 0)
		rect.setWidth(fixedWidth);
	if (fixedHeight > 0)
		rect.setHeight(fixedHeight);

	return rect.size();
}

BSize member_BHtmlItem::freshListSize(int width)
{
	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
	rect.reset();
	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* item = boss->child(i);
		BSize itemSize = item_member(item)->freshSize(0);
		if (rect.width() < itemSize.width())
			rect.setWidth(itemSize.width());
		rect.bottom() += itemSize.height();
	}
	int x = rect.left();
	int y = rect.top();
	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* item = boss->child(i);
		int height = item_member(item)->rect.height();
		item_member(item)->adjust(x, y, rect.width(), height);
		y += height;
	}
	return rect.size();
}
BSize member_BHtmlItem::freshTableSize(int width)
{
	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
	BIntArray columnSizes;
	BIntArray rowSizes(children.size());
	for (int r = 0; r < children.size(); r++)
	{
		BHtmlItem* rowItem = boss->child(r);
		int column = rowItem->childCount();
		if (columnSizes.size() < column)
			columnSizes.resize(column);
		for (int c = 0; c < rowItem->childCount(); c++)
		{
			BHtmlItem* item = rowItem->child(c);
			BSize size = item_member(item)->freshSize(0);
			if (rowSizes[r] < size.height())
				rowSizes[r] = size.height();
			if (columnSizes[c] < size.width())
				columnSizes[c] = size.width();
		}
	}

	for (int r = 0; r < rowSizes.size(); r++)
	{
		rect.bottom() += rowSizes[r];
	}
	for (int c = 0; c < columnSizes.size(); c++)
	{
		rect.right() += columnSizes[c];
	}
	int x = 0;
	int y = block.rect.height();
	for (int r = 0; r < children.size(); r++)
	{
		BHtmlItem* row = children[r].as<BHtmlItem>();
		x = 0;
		int rowSize = rowSizes[r];
		item_member(row)->adjust(x, y, rect.width(), rowSize);
		for (int c = 0; c < row->childCount(); c++)
		{
			int columnSize = columnSizes[c];
			BHtmlItem* item = row->child(c);
			item_member(item)->adjust(x, 0, columnSize, rowSize);
			x += columnSize;
		}
		y += rowSizes[r];
	}
	return rect.size();
}
BSize member_BHtmlItem::freshTreeSize(int width)
{
	rect.setSize(100, 100);
	return rect.size();
}

void member_BHtmlItem::adjust(int x, int y, int w, int h)
{
	rect.set(x, y, w, h);
	block.dirtyCells = true;
	block.align = align;
	block.adjust(w);
}
void member_BHtmlItem::adjust(const BPoint& pos, const BSize& size)
{
	adjust(pos.x(), pos.y(), size.width(), size.height());
}
